package fight

import (
	"sync"
	"sync/atomic"
	"time"
	// "fsd/logger"
	// "go.uber.org/zap"
)

type GameState int32

const (
	// 初始化等待登陆
	GAME_STATE_INIT int32 = iota
	// 等待loading
	GAME_STATE_LOADING
	// 等待客户端时间轴
	GAME_STATE_TIMELINE
	// 服务端启动时间轴, 战斗正式开始
	GAME_STATE_START
	// 结束阶段
	GAME_STATE_SETTLE
	// 战斗结束
	GAME_STATE_OVER
)

type Game struct {
	id          uint64
	players     map[int32]*Player
	closeChan   chan struct{}
	C2SMsgChan  chan *WrapMsg
	cacheFrame  *WrapMsg
	settlements *WrapMsg
	round       int32
	state       int32
	gameTime    *GameTime
	closeOnce   sync.Once
	closeFlag   int32
}

type GameTime struct {
	gameStartNS  int64        // Game创建时的时间
	fightStartNS int64        // 服务器时间轴启动时的时间戳
	recvNS       int64        // 接收客户端上报帧时的时间戳
	frameTimer   *time.Timer  // 帧定时器
	settleTimer  *time.Timer  // 战斗结束等待定时器
	loopTicker   *time.Ticker // 收包检查定时器
}

const (
	PLAYER_STATE_INIT = iota
	// 登陆完成
	PLAYER_STATE_LOGINED
	// 加载完成
	PLAYER_STATE_LOADED
	// 时间轴已启动
	PLAYER_STATE_START
)

type Player struct {
	userId   int32
	conn     *Conn
	state    int
	sendFlag bool
}

func NewGame(id uint64, userIds []int32) {
	cf, _ := NewS2CWrapMsg(PT_CMD_BROADCAST, Srv.GameProtocoler)
	st, _ := NewS2CWrapMsg(PT_CMD_SETTLE, Srv.GameProtocoler)
	g := &Game{
		id:         id,
		players:    make(map[int32]*Player),
		closeChan:  make(chan struct{}),
		C2SMsgChan: make(chan *WrapMsg, Srv.PutGameChanSize),
		gameTime: &GameTime{
			gameStartNS: time.Now().UnixNano(),
			loopTicker:  time.NewTicker(Srv.GameTickerTime),
			frameTimer:  &time.Timer{},
			settleTimer: &time.Timer{},
		},
		cacheFrame:  cf,
		settlements: st,
		round:       1,
	}
	for _, userId := range userIds {
		g.players[userId] = &Player{
			userId: userId,
		}
	}
	asyncStartChild(&Srv.gameWG, g.start)
}

// 启动战斗协程
func (g *Game) start() {
	defer func() {
		g.stop()
        GameMgr.Del(g.id)
        // 通知Conn关闭
        for _, p := range g.players {
            if p.conn != nil {
                close(p.conn.getGameChan)
            }
        }
        g.stopTimer()
	}()

	GameMgr.Put(g.id, g)
	for {
		select {
        case <-Srv.exitChan:
            return
            
		case <-g.closeChan:
			return

		case wm := <-g.C2SMsgChan:
			g.routeHandleC2SMsg(wm)

		case t := <-g.gameTime.loopTicker.C:
			if t.UnixNano()-g.gameTime.recvNS > int64(time.Minute) {
				g.gameTime.settleTimer = time.NewTimer(Srv.GameSettleTime)
			}

		case <-g.gameTime.frameTimer.C:
			nowNS := time.Now().UnixNano()
			g.updateFrameTimer(nowNS)
			g.handleFrameData(nowNS)

		case <-g.gameTime.settleTimer.C:
			g.over()
			return
		}
	}
}

// 手动关闭战斗调用该接口
func (g *Game) stop() {
	g.closeOnce.Do(func() {
		atomic.StoreInt32(&g.closeFlag, 1)
		close(g.closeChan)
	})
}

// C->Game 协议包路由
func (g *Game) routeHandleC2SMsg(c2sMsg *WrapMsg) {
	switch c2sMsg.Cmd {
	case PT_CMD_LOGIN:
		g.handleC2SMsgLogin(c2sMsg)
	case PT_CMD_LOADING:
		g.handleC2SMsgLoading(c2sMsg)
	case PT_CMD_TIMELINE:
		g.handleC2SMsgTimeline(c2sMsg)
	case PT_CMD_UPLOAD:
		g.handleC2SMsgUpload(c2sMsg)
	case PT_CMD_SETTLE:
		g.handleC2SMsgSettle(c2sMsg)
	}
}

// 更新帧回合定时器
func (g *Game) updateFrameTimer(nowNS int64) {
	delta := (nowNS - g.gameTime.fightStartNS) - int64(g.round)*Srv.GameRoundTime
	var d int64
	if delta < Srv.GameRoundTime {
		d = Srv.GameRoundTime - delta
	}
	// if !g.gameTime.frameTimer.Stop() {
	//     <-g.gameTime.frameTimer.C
	// }
	g.gameTime.frameTimer.Reset(time.Duration(d))
}

// 关闭定时器
func (g *Game) stopTimer() {
    // 关闭帧回合定时器
	if g.gameTime.frameTimer.C != nil {
        g.gameTime.frameTimer.Stop()
	}
    // 关闭loopTcker
    g.gameTime.loopTicker.Stop()
    // 关闭结算定时器
	if g.gameTime.settleTimer.C != nil {
        g.gameTime.settleTimer.Stop()
	}
}

// 客户端登陆
func (g *Game) handleC2SMsgLogin(wmReq *WrapMsg) {
	if p, ok := g.players[wmReq.C.connInfo.userId]; ok {
		// 登陆处理成功
		if g.state == GAME_STATE_INIT && p.state == PLAYER_STATE_INIT {
			p.state = PLAYER_STATE_LOGINED
		}

		wmAck, _ := NewS2CWrapMsg(PT_CMD_LOGIN, Srv.GameProtocoler)
		wmAck.Msg.SetLoginAckRet(PT_ACK_RET_OK)
		wmAck.Msg.SetLoginAckGameState(g.state)
		err := wmAck.NewEncodeWrapMsg()
		if err == nil {
			p.conn = wmReq.C
			p.conn.getGameChan <- wmAck
			g.maybeAllLogined()
			return
		}
	}
	wmReq.C.getGameChan <- PreS2CMsgFightLoginAckFail
}

// 检查所有客户端登陆状态
func (g *Game) maybeAllLogined() {
	if g.state != GAME_STATE_INIT {
		return
	}

	for _, p := range g.players {
		if p.state != PLAYER_STATE_LOGINED {
			return
		}
	}
	g.state = GAME_STATE_LOADING
	g.broadcastS2CMsg(PreS2CMsgFightAllLogined)
}

// 客户端loading完成
func (g *Game) handleC2SMsgLoading(wmReq *WrapMsg) {
	if p, ok := g.players[wmReq.C.connInfo.userId]; ok {
		if g.state == GAME_STATE_LOADING && p.state == PLAYER_STATE_LOGINED {
			p.state = PLAYER_STATE_LOADED
		}
		wmReq.C.getGameChan <- PreS2CMsgFightLoadAckOK
		g.maybeAllLoad()
		return
	}
	wmReq.C.getGameChan <- PreS2CMsgFightLoadAckFail
}

// 检查所有客户端loading状态
func (g *Game) maybeAllLoad() {
	if g.state != GAME_STATE_LOADING {
		return
	}

	for _, p := range g.players {
		if p.state != PLAYER_STATE_LOADED {
			return
		}
	}
	g.state = GAME_STATE_TIMELINE
	g.broadcastS2CMsg(PreS2CMsgFightAllLoaded)
}

// 客户端开启时间轴完成
func (g *Game) handleC2SMsgTimeline(wmReq *WrapMsg) {
	if p, ok := g.players[wmReq.C.connInfo.userId]; ok {
		if g.state == GAME_STATE_TIMELINE && p.state == PLAYER_STATE_LOADED {
			p.state = PLAYER_STATE_START
		}
		p.conn.getGameChan <- PreS2CMsgFightTimelineAckOK
		g.maybeGameTimeline()
		return
	}
	wmReq.C.getGameChan <- PreS2CMsgFightTimelineAckFail
}

// 检查所有客户端时间轴开启状态状态
func (g *Game) maybeGameTimeline() {
	if g.state != GAME_STATE_TIMELINE {
		return
	}

	for _, p := range g.players {
		if p.state != PLAYER_STATE_START {
			return
		}
	}
	g.state = GAME_STATE_START
	g.gameTime.fightStartNS = time.Now().UnixNano()
	g.gameTime.frameTimer = time.NewTimer(time.Duration(Srv.GameRoundTime)) // 启动帧定时器
}

// 广播一回合内的数据帧
func (g *Game) handleFrameData(now int64) {
	for _, p := range g.players {
		if p.sendFlag {
			continue
		}
		wm0, _ := NewC2SWrapMsg(PT_CMD_UPLOAD, Srv.GameProtocoler)
		if p.conn == nil || p.conn.IsClosed() {
			wm0.Msg.SetUploadFrameCmdId(FIGHT_FRAME_CMD_ID_OFFLINE)
		} else {
			wm0.Msg.SetUploadFrameCmdId(FIGHT_FRAME_CMD_ID_NULL)
		}
		g.cacheFrame.Msg.AppendBroadcastFrameDatas(p.userId, wm0.Msg)
	}
	g.cacheFrame.Msg.SetBroadcastFrameId(g.round)
	g.cacheFrame.Msg.SetBroadcastFrameTimeline(int32((now - g.gameTime.fightStartNS) / 1000000))
	err := g.cacheFrame.NewEncodeWrapMsg()
	if err == nil {
		g.broadcastS2CMsg(g.cacheFrame)
		// TODO: store all frame
	}
	g.round++
	g.cacheFrame, _ = NewS2CWrapMsg(PT_CMD_BROADCAST, Srv.GameProtocoler)
	for _, p := range g.players {
		p.sendFlag = false
	}
}

// 缓存当前回合内客户端上报的数据
func (g *Game) handleC2SMsgUpload(wmReq *WrapMsg) {
	if p, ok := g.players[wmReq.C.connInfo.userId]; ok {
		p.sendFlag = true
		g.gameTime.recvNS = time.Now().UnixNano()
		g.cacheFrame.Msg.AppendBroadcastFrameDatas(p.userId, wmReq.Msg)
	}
}

// 客户端上报战斗结束
func (g *Game) handleC2SMsgSettle(wmReq *WrapMsg) {
	if p, ok := g.players[wmReq.C.connInfo.userId]; ok {
		g.settlements.Msg.AppendSettleAckResults(p.userId, wmReq.Msg)
		if g.state != GAME_STATE_SETTLE {
			g.state = GAME_STATE_SETTLE
			g.gameTime.settleTimer = time.NewTimer(Srv.GameSettleTime)
		}
	}
}

// 战斗结束
func (g *Game) over() {
	if g.state == GAME_STATE_OVER {
		return
	}
	g.state = GAME_STATE_OVER

	err := g.settlements.NewEncodeWrapMsg()
	if err != nil {
		g.broadcastS2CMsg(PreS2CMsgFightSettleAckFail)
	} else {
		g.broadcastS2CMsg(g.settlements)
	}
}

// 广播S2CMsg
func (g *Game) broadcastS2CMsg(wm *WrapMsg) {
	for _, p := range g.players {
		if p.conn == nil || p.conn.IsClosed() {
			continue
		}
		p.conn.getGameChan <- wm
	}
}
